package com.nulldev.util.web.YouTubeExtractor.signatures.extractors.v9;

import java.io.Closeable;
import java.io.IOException;

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Returnable;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.io.FastIO.FastIO;
import com.nulldev.util.scripting.ScriptAPI.nScripting;
import com.nulldev.util.web.YouTubeExtractor.signatures.ExtractedSignatureData;
import com.nulldev.util.web.YouTubeExtractor.signatures.extractor.Extractor;
import com.nulldev.util.web.YouTubeExtractor.signatures.extractor.V9Extractor;
import com.nulldev.util.web.YouTubeExtractor.testing.tests.DeciphererV9Tests;

public class RhinoBasedV9Extractor extends V9Extractor implements Closeable {

	private ScriptEngine ns;
	private final ScriptContext ctx;
	private String fallback;

	public RhinoBasedV9Extractor(final ExtractedSignatureData data) {
		super(data);
		if (!nScripting.isRhinoInstalled()) {
			throw new RuntimeException("Extractors.Rhino --> Rhino isn't installed.");
		}
		this.fallback = SharedV9ExtractorUtil.getFallbackPayload(RhinoBasedV9Extractor.this.getCode());
		{
			this.ctx = new SimpleScriptContext();
			RhinoBasedV9Extractor.this.ns = nScripting.getEngineByName("Rhino");
			if (RhinoBasedV9Extractor.this.ns != null) {
				try {
					RhinoBasedV9Extractor.this.ns.eval(RhinoBasedV9Extractor.this.getCode(), this.ctx);
				} catch (ScriptException e) {
					JVM.errorln(e);
				}
			}
		}
	}

	@Override
	public String decrypt(final String signature) {
		if (signature == null)
			return null;

		try {
			return (String) RhinoBasedV9Extractor.this.ns.eval(RhinoBasedV9Extractor.this.getFunctionName() + "(\"" + signature + "\");", this.ctx);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Executable<String> decryptTask(final String signature) {
		return ExecutorManager.make(() -> {
			return decrypt(signature);
		});
	}

	@Override
	public String getCode() {
		return this.getExtractedData().getCode();
	}

	@Override
	public String getFunctionCode() {
		return this.getExtractedData().getFunctionCode();
	}

	@Override
	public String getFunctionName() {
		return this.getExtractedData().getFunctionName();
	}

	@Override
	public Extractor updateESD(final ExtractedSignatureData newData) {
		super.setExtractedData(newData);
		if (RhinoBasedV9Extractor.this.ns != null) {
			try {
				RhinoBasedV9Extractor.this.ns.eval("delete " + RhinoBasedV9Extractor.this.getFunctionName(), this.ctx);
				RhinoBasedV9Extractor.this.ns.eval(RhinoBasedV9Extractor.this.getCode(), this.ctx);
			} catch (ScriptException e) {
				JVM.errorln(e);
			}
		}
		return this;
	}

	@Override
	public void close() throws IOException {
	}

	@Override
	public String makeFallback(String payload) {
		return SharedV9ExtractorUtil.makeFallback(this.fallback, payload);
	}

	@Override
	public Executable<String> makeFallbackTask(String signature) {
		return ExecutorManager.make(new Returnable<String>() {

			@Override
			public String run() {
				return makeFallback(signature);
			}
		});
	}

	public static void main(String[] args) {
		Arguments.setup(args);
		final RhinoBasedV9Extractor rhne = new RhinoBasedV9Extractor(DeciphererV9Tests.ESD);
		final String out = rhne.decrypt("TxrJXs9V6hYCRR2Alr1xj");
		JVM.println("out = " + out);
		FastIO.get().flush();
		IOUtils.closeQuietly(rhne);
		JVM.exit(-1);
	}
}
